feat(agent): add extensible web workbench and plugins - #65
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bd7d691a56
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @@ -1,2 +1,13 @@ | |||
| export { startMossWebServer } from './web-server.js'; | |||
| export type { MossWebServerHandle, MossWebServerOptions } from './web-server.js'; | |||
| export { MOSS_WEB_SLOTS, MOSS_WEB_THEME_TOKENS } from './web-contracts.js'; | |||
There was a problem hiding this comment.
Re-export the documented Web server API
The ./web barrel no longer exports startMossWebServer, MossWebServerHandle, or MossWebServerOptions, even though this commit promotes the server to @beta and documents these symbols as the supported @rdk-moss/agent/web API. Consequently, both existing imports and the newly documented embedding path fail because the package subpath exposes only constants and DTO types. Restore the server exports and cover them in web-public-api.spec.mjs.
AGENTS.md reference: AGENTS.md:L144-L144
Useful? React with 👍 / 👎.
| ); | ||
| } | ||
| await mkdir(this.npmRoot, { recursive: true }); | ||
| await this.npmRunner('npm', { |
There was a problem hiding this comment.
Invoke the npm shim correctly on Windows
When a Windows user runs moss plugins add package@version, this calls runProcess('npm', ...), whose spawn path does not enable a shell and therefore cannot execute the standard npm.cmd shim. The repository already handles this exact constraint in src/cli/update.ts by enabling the shell for the default npm command; use the same platform-aware approach here so npm-backed plugin installation does not fail with ENOENT on Windows.
AGENTS.md reference: AGENTS.md:L57-L57
Useful? React with 👍 / 👎.
| response: http.ServerResponse, | ||
| filename: 'workbench.css' | 'workbench.js' | ||
| ): Promise<void> { | ||
| const body = await readFile(new URL(`./client/${filename}`, import.meta.url), 'utf8'); |
There was a problem hiding this comment.
Serve the workbench when running the source CLI
In the documented npm run cli -- web development path, import.meta.url points into src/web-ui, so this attempts to read src/web-ui/client/workbench.js; only workbench.tsx exists there because the JavaScript bundle is generated under dist during a build. The HTML loads but the script request fails, leaving a blank workbench. Either resolve built assets independently of the executing source path or provide a source-mode asset build/loader.
AGENTS.md reference: packages/moss-agent/AGENTS.md:L17-L17
Useful? React with 👍 / 👎.
| useEffect(() => { | ||
| void refresh().catch(() => setOnline(false)); | ||
| const onlineListener = () => void refresh().catch(() => setOnline(false)); | ||
| const offlineListener = () => setOnline(false); |
There was a problem hiding this comment.
Restore automatic retries after a transient disconnect
If the initial refresh or a later request fails while the browser remains network-online—for example, while the local Moss process restarts—this only sets online to false and retries solely on a browser online event. A localhost process restart does not normally change browser connectivity, so the page remains permanently in “Reconnecting” state after the server returns. Schedule a bounded refresh retry as the previous client did, and cancel it during cleanup.
Useful? React with 👍 / 👎.
| const root = await this.resolveSource(source); | ||
| const manifest = await readMossPluginManifest(root); | ||
| const existing = (await this.readRegistry()).plugins; | ||
| if (existing.some(({ id }) => id === manifest.id)) { | ||
| throw invalidManifest(`plugin already installed: ${manifest.id}`); |
There was a problem hiding this comment.
Check duplicates before mutating shared npm installs
For an npm source, resolveSource() installs into the shared prefix before the registry checks whether the manifest ID is already installed. If an enabled foo@1.0.0 is registered and the user attempts moss plugins add foo@2.0.0, npm replaces node_modules/foo with v2, then this duplicate check reports failure and leaves the v1 registry record untouched. On the next startup that supposedly failed and disabled-by-default addition is nevertheless loaded as v2. Install into a staging location and validate duplicates before replacing any path referenced by the live registry.
Useful? React with 👍 / 👎.
| export async function runPluginsCommand(args: readonly string[]): Promise<void> { | ||
| const registry = new InstalledPluginRegistry({ configDir: resolveConfigDir() }); |
There was a problem hiding this comment.
Honor --config-file for plugin registry storage
When the CLI is invoked with --config-file /path/to/config.json, the dispatcher loads that file but this command still constructs the registry under the default resolveConfigDir(). Agent startup and the Web host likewise load plugins from the default directory, so plugin operations performed for an explicit portable config are written to and composed from an unrelated registry. Pass the resolved config path's directory through the command and runtime wiring, as other config-owned state does.
Useful? React with 👍 / 👎.
| <button | ||
| className={session.sessionId === sessionId ? 'active' : ''} | ||
| key={session.sessionId} | ||
| onClick={() => void openSession(session)} |
There was a problem hiding this comment.
Isolate a running stream from session navigation
While a turn is running, the recent-session buttons remain enabled and openSession() replaces the global sessionId and items, but the old fetch continues feeding applyStreamEvent() into that same global timeline. Selecting another session therefore displays the previous session's streamed text and tools in the newly selected conversation, and Stop then targets the newly selected session instead of the actual running one. Disable or cancel-and-await navigation during a turn, or key stream state and cancellation by the originating session.
Useful? React with 👍 / 👎.
|
|
||
| async function validatePluginSetup(entry: InstalledMossPlugin): Promise<void> { | ||
| const manifest = await readMossPluginManifest(entry.root); | ||
| const worker = new Worker(new URL('./plugin-setup-worker.js', import.meta.url), { |
There was a problem hiding this comment.
Resolve the setup worker when running the source CLI
In the documented source-mode CLI, import.meta.url points into src/plugins, but this constructs a worker URL for src/plugins/plugin-setup-worker.js; only plugin-setup-worker.ts exists until the package is built. As a result, npm run cli -- plugins enable <id> and plugins doctor reject every setup validation with a worker module-not-found error. Resolve a source-compatible worker entry or ensure the development command builds and uses the emitted worker first.
AGENTS.md reference: packages/moss-agent/AGENTS.md:L17-L17
Useful? React with 👍 / 👎.
| async function snapshotActiveWebContributions( | ||
| registry: InstalledPluginRegistry | undefined, | ||
| activePluginIds: ReadonlySet<string> | ||
| ): Promise<readonly ActiveWebContribution[]> { | ||
| if (!registry) return Object.freeze([]); |
There was a problem hiding this comment.
Make registered Web contributions consumable
For an embedding host that installs a MossPlugin using context.registerWebContribution() and then starts the Web server without an installed-plugin registry, this immediately returns no contributions. Even when configDir is supplied, the server rebuilds contributions only from moss.plugin.json and never consumes the definitions staged on the active plugin host, whose snapshot retains only their IDs. Thus the newly public registration API cannot mount any programmatic plugin UI; preserve and resolve the staged contribution records or narrow the public contract to manifest-only contributions.
AGENTS.md reference: AGENTS.md:L144-L144
Useful? React with 👍 / 👎.
| if (active.has(sessionId)) { | ||
| return sendJson(response, 409, { error: 'session already has an active turn' }); |
There was a problem hiding this comment.
Allow a reloaded browser to control an active turn
If the page reloads or its streaming connection drops during a long turn, the server keeps the session in active, but the new workbench initializes its local running state to false and does not derive it from the running task snapshot. The Stop control is therefore absent, while any attempted prompt receives this 409 and is treated by the client as a connection failure. Until the old model call finishes, the user can neither reconnect to nor cancel it; expose active-turn state and cancellation after bootstrap, or abort the turn when its response closes.
Useful? React with 👍 / 👎.
feat(agent): add extensible web workbench and plugins
Summary
official:deepseek-harnessVerification
npm run verifyNotes
The Web UI keeps Moss branding while aligning the reference workbench interaction model. Third-party plugin JavaScript remains explicitly trusted code and is never described as sandboxed.